/*! @file
********************************************************************************
<PRE>
模块名       : <DNS解析模块>
文件名       : <dnspacket.h>
相关文件     : 
文件实现功能 : <解析DNS数据包>
作者         : <张则梁>
版本         : <1.0>
--------------------------------------------------------------------------------
多线程安全性 : <是/否>[，说明]
异常时安全性 : <是/否>[，说明]
--------------------------------------------------------------------------------
备注         : <其它说明>
--------------------------------------------------------------------------------
修改记录 : 
日 期        版本     修改人              修改内容
YYYY/MM/DD   X.Y      <作者或修改者名>    <修改内容>
</PRE>
*******************************************************************************/

#ifndef __DNSPACKET_H__
#define __DNSPACKET_H__

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
#include <vector>

using namespace std;

#define DEBUG_PRINT

#define UDP_PORT_DNS    53              /* DNS端口 */

#define MAXDNSNAME      256             /* 最大域名长度 */
#define MAXCHARSTR      256             /* 最大character-string长度 */
#define DNS_HDRLEN      12              /* DNS头部长度 */

#define MAX_RRS_NUM     50              /* 最大资源记录数 */
#define MAX_RDATA_LEN   10240           /* RDATA最大长度 */

/* TYPE值  */
#define T_A             1               /* 主机地址 */
#define T_NS            2               /* 权威名称服务器 */
#define T_MD            3               /* 邮件目的地(被废弃，使用MX) */
#define T_MF            4               /* 邮件转发器(被废弃，使用MX) */
#define T_CNAME         5               /* 别名的正则名称 */
#define T_SOA           6               /* 标记权威区域的开始 */
#define T_MB            7               /* 邮箱域名(试验) */
#define T_MG            8               /* 邮件组成员(试验) */
#define T_MR            9               /* 邮件重新命名域名(试验) */
#define T_NULL          10              /* 空RR(试验) */
#define T_WKS           11              /* 众所周知的业务描述 */
#define T_PTR           12              /* 域名指针 */
#define T_HINFO         13              /* 主机信息 */
#define T_MINFO         14              /* 邮箱或邮件列表信息 */
#define T_MX            15              /* 邮件交换 */
#define T_TXT           16              /* 文本字符串 */
#define T_AAAA          28              /* IPV6地址 */
#define T_AXFR          252             /* 请求整个区域传送 */
#define T_MAILB         253             /* 请求相关邮箱记录(MB、MG或MR) */
#define T_MAILA         254             /* 请求邮件代理RRs(被废弃，参阅MX) */
#define T_ANY           255             /* 请求所有记录 */


/* CLASS值 */
#define C_IN            1               /* 互联网 */
#define C_CS            2               /* CSNET类(被废弃，仅在某些被废弃的RFCs中用于举例) */
#define C_CH            3               /* CHAOS类 */
#define C_HS            4               /* 赫西奥德(Hesiod)[Dyer 87] */
#define C_ANY           5               /* 任何类 */

/* OPCODE */
#define OPCODE_QUERY    0               /* 标准查询(QUERY) */
#define OPCODE_IQUERY   1               /* 反向查询(IQUERY) */
#define OPCODE_STATUS   2               /* 服务器状态请求(STATUS) */
/* 3-15 保留将来使用 */

/* RCODE */
#define RCODE_NOERROR   0               /* 没有出错条件 */
#define RCODE_FORMERR   1               /* 格式错误---名称服务器不能解释查询 */
#define RCODE_SERVFAIL  2               /* 服务器故障---由于与名称服务器有关的问题，名称服务器不能处理这个查询 */
#define RCODE_NXDOMAIN  3               /* 名称错误---仅对来自权威名称服务器的响应有意义，这个代码预示在该查询中被查询的域名不存在 */
#define RCODE_NOTIMPL   4               /* 未实现---名称服务器不支持请求的查询种类 */
#define RCODE_REFUSED   5               /* 拒绝---由于策略原因名称服务器拒绝执行指定的操作 */
/* 6-15 保留将来使用 */

/* 返回值 */
enum
{
    E_RET_SUCCESS = 0,          /* 成功 */
    E_RET_FAIL = 1,             /* 失败 */
    E_RET_HEAD_FAIL = 2,        /* 解析包头失败 */
    E_RET_ERRRES = 3,           /* 错误应答码 */
};

#pragma pack(1)

struct IP_ADDR
{
    union{
    uint32_t addr4;
	struct in6_addr addr6;
    };
};

/* DNS包头信息:总共12字节的包头，解析成此结构体*/
struct DNS_HEAD_INFO
{
    uint16_t id;            /* 由程序分配的16位标识符 */
    uint16_t flag;          /* QR---1位字段，查询(0)还是响应(1) */
                            /* OPCODE---4位字段，查询的种类 */
                            /* AA---1位字段，权威回答 */
                            /* TC---1位字段，截断 */
                            /* RD---1位字段，期望递归 */
                            /* RA---1位字段，递归可用 */
                            /* z---3位字段，保留将来使用 */
                            /* RCODE---4位字段，响应代码 */
    uint16_t qdcount;       /* 无符号16位整数表示报文请求段中的问题记录数 */
    uint16_t ancount;       /* 无符号16位整数表示报文回答段中的回答记录数 */
    uint16_t nscount;       /* 无符号16位整数表示报文授权段中的授权记录数 */
    uint16_t arcount;       /* 无符号16位整数表示报文附加段中的附加记录数 */
};

/* 问题信息 */
struct DNS_QUESTION_INFO
{
    uint8_t name[MAXDNSNAME];   /* 域名 */
    uint16_t type;              /* 2个字节表示查询类型 */
    uint16_t cls;               /* 2个字节表示查询的协议类 */
};

/* 资源记录:应答，授权，附加段都共用相同的格式 */
struct DNS_RESOURCE_INFO
{
    uint8_t name[MAXDNSNAME];   /* 资源记录包含的域名 */
    uint16_t type;              /* 2个字节表示资源记录的类型 */
    uint16_t cls;               /* 2个字节表示RDATA的类 */
    uint32_t ttl;               /* 4字节无符号整数表示资源记录可以缓存的时间 */
    uint16_t rdlen;             /* 2个字节无符号整数表示RDATA的长度 */
    int32_t rdoffset;           /* RDATA的起始偏移 */
    uint8_t rdatainfo[MAX_RDATA_LEN];         /* 解析后的RDATA，格式看具体类型 */
};

/* A RDATA格式 */
struct RDATA_FORMAT_A
{
    uint32_t address;                       /* 32位互联网地址 */
};

/* NS RDATA格式 */
struct RDATA_FORMAT_NS
{
    uint8_t nsdname[MAXDNSNAME];            /* domain-name */
};

/* MD RDATA格式 or MF RDATA格式 被废弃 */
struct RDATA_FORMAT_MDF
{
    uint8_t madname[MAXDNSNAME];            /* domain-name */
};

/* CNAME RDATA格式 */
struct RDATA_FORMAT_CNAME
{
    uint8_t cname[MAXDNSNAME];              /* domain-name */
};

/* SOA RDATA格式 */
struct RDATA_FORMAT_SOA
{
    uint8_t mname[MAXDNSNAME];              /* domain-name */
    uint8_t rname[MAXDNSNAME];              /* domain-name */
    uint32_t serial;                        /* 该区域的原始副本的无正负号32位版本号 */
    int32_t refresh;                        /* 区域应当被刷新前的32位时间间隔 */
    int32_t retry;                          /* 应当重试失败的刷新前，应当消逝的32位时间间隔 */
    int32_t expire;                         /* 32 位时间值，它规定时间间隔 */
    uint32_t minimum;                       /* 无正负号32位最小值TTL字段 */
};

/* MB RDATA格式 试验*/
struct RDATA_FORMAT_MB
{
    uint8_t madname[MAXDNSNAME];            /* domain-name */
};

/* MG RDATA格式 试验*/
struct RDATA_FORMAT_MG
{
    uint8_t mgmname[MAXDNSNAME];            /* domain-name */
};

/* MR RDATA格式 试验*/
struct RDATA_FORMAT_MR
{
    uint8_t newname[MAXDNSNAME];            /* domain-name */
};

/* NULL RDATA格式 试验 RDATA字段中可以放任何东西*/
/* 返回RDATA数据 自己去解析 */
struct RDATA_FORMAT_NULL
{
    uint8_t *pData;                         /* 数据指针 */
    int32_t len;                            /* 数据长度 */
};

/* WKS RDATA格式 */
struct RDATA_FORMAT_WKS
{
    uint32_t address;                       /* 32位互联网地址 */
    uint8_t protocol;                       /* 8位IP协议号 */
    uint8_t *pbitmap;                       /* 可变长度位图。该位图必须是8位长的倍数 */
    int32_t bmplen;                         /* 位图数据长度 */
};

/* PTR RDATA格式 */
struct RDATA_FORMAT_PTR
{
    uint8_t ptrdname[MAXDNSNAME];           /* domain-name */
};

/* HINFO RDATA格式 */
struct RDATA_FORMAT_HINFO
{
    uint8_t cpu[MAXCHARSTR];                /* CPU的类型 */
    int32_t clen;                           /* cpu类型字段长度 */
    uint8_t os[MAXCHARSTR];                 /* 操作系统类型的类型 */
    int32_t olen;                           /* 操作系统类型字段长度 */
};

/* MINFO RDATA格式 */
struct RDATA_FORMAT_MINFO
{
    uint8_t rmailbx[MAXDNSNAME];            /* domain-name */
    uint8_t emailbx[MAXDNSNAME];            /* domain-name */
};

/* MX RDATA格式 */
struct RDATA_FORMAT_MX
{
    int16_t preference;                     /* 16位整数，它规定在同一所有者内，众多RR间，给与这个RR的优先权 */
    uint8_t exchange[MAXDNSNAME];           /* domain-name */
};

/* TXT RDATA格式 一个或多个<character-string>s */
/* 返回RDATA数据 自己去解析 */
struct RDATA_FORMAT_TXT
{
    uint8_t *pData;                         /* 数据指针 */
    int32_t len;                            /* 数据长度 */
};

#pragma pack()

/*! @class
********************************************************************************
<PRE>
类名称   : CDnsPacket
功能     : <DNS数据包解析类>
异常类   : <属于该类的异常类（如果有的话）> 
--------------------------------------------------------------------------------
备注     : <使用该类时需要注意的问题（如果有的话）> 
典型用法 : <如果该类的使用方法较复杂或特殊，给出典型的代码例子> 
--------------------------------------------------------------------------------
作者     : <张则梁>, [yyy], [zzz] ...（作者和逗号分割的修改者列表）
</PRE>
*******************************************************************************/
class CDnsPacket
{
public:
///////////////////////////////////////////////////////////// 构造、析构、初始化
    CDnsPacket();
    ~CDnsPacket();
    /* 初始化数据 */
    int32_t InitData(uint8_t *data, uint32_t len);

public:
/////////////////////////////////////////////////////////////////////// 公用方法
    /* 获取头信息结构指针 */
    DNS_HEAD_INFO* GetHeadInfo(void);
    /* 获取问题信息结构指针 */
    DNS_QUESTION_INFO* GetQueriesInfo(void);
    /* 获取回答信息结构指针 */
    DNS_RESOURCE_INFO* GetAnswersInfo(void);
    /* 获取授权信息结构指针 */
    DNS_RESOURCE_INFO* GetAuthorsInfo(void);
    /* 获取附加信息结构指针 */
    DNS_RESOURCE_INFO* GetAdditionsInfo(void);

    /* 从flag中获取QR值 */
    static uint16_t GetQrFromFlag(uint16_t flag);
    /* 从flag中获取OPCODE值 */
    static uint16_t GetOpcodeFromFlag(uint16_t flag);
    /* 从flag中获取AA值 */
    static uint16_t GetAaFromFlag(uint16_t flag);
    /* 从flag中获取TC值 */
    static uint16_t GetTcFromFlag(uint16_t flag);
    /* 从flag中获取RD值 */
    static uint16_t GetRdFromFlag(uint16_t flag);
    /* 从flag中获取RA值 */
    static uint16_t GetRaFromFlag(uint16_t flag);
    /* 从flag中获取RCODE值 */
    static uint16_t GetRcodeFromFlag(uint16_t flag);
    
    /* 是否合法域名 */
    static int8_t IsLegelDomainName(char *dname);
    
#ifdef DEBUG_PRINT    
    /* 打印头信息 */
    void PrintHeadInfo(DNS_HEAD_INFO *info);
    /* 打印问题信息 */
    void PrintQuestionInfo(DNS_QUESTION_INFO *info);
    /* 打印资源信息 */
    void PrintResourceInfo(DNS_RESOURCE_INFO *info);
    /* 打印DNS所有信息 */
    void DebugPrintDnsInfo(void);
#endif
    
private:
/////////////////////////////////////////////////////////////////////////// 属性
    uint8_t *m_data;                /* 数据包 */
    int32_t m_dlen;                 /* 数据包长度 */
    DNS_HEAD_INFO m_head_inf;       /* 包头信息 */
    DNS_QUESTION_INFO m_queries[MAX_RRS_NUM];   /* 问题信息 */
    DNS_RESOURCE_INFO m_answers[MAX_RRS_NUM];   /* 回答信息 */
    DNS_RESOURCE_INFO m_authors[MAX_RRS_NUM];   /* 授权信息 */
    DNS_RESOURCE_INFO m_additions[MAX_RRS_NUM]; /* 附加信息 */

private:
///////////////////////////////////////////////////////////////////// 禁用的方法
    void __FreeResourceInfo(DNS_RESOURCE_INFO resinfo[MAX_RRS_NUM], int32_t num);
    int32_t __ParserHead(void);
    int32_t __ExpandDnsName(int32_t *offset, uint8_t *name);
    int32_t __GetOneQuestion(int32_t *offset, DNS_QUESTION_INFO* queinfo);
    int32_t __GetOneResource(int32_t *offset, DNS_RESOURCE_INFO* resinfo);
    /* 解析RDATA数据 InitData函数没有解析RDATA数据，调用此接口后才解析RDATA数据 */
    int32_t __ParseAllRdata(void);
    int32_t __ParseRdata(DNS_RESOURCE_INFO *resinfo);
    
    /* ----------解析各类RDATA数据begin----------- */
    int32_t __Rdata2A(DNS_RESOURCE_INFO *resinfo);
    int32_t __Rdata2NS(DNS_RESOURCE_INFO *resinfo);
    int32_t __Rdata2MD(DNS_RESOURCE_INFO *resinfo);
    int32_t __Rdata2MF(DNS_RESOURCE_INFO *resinfo);
    int32_t __Rdata2CNAME(DNS_RESOURCE_INFO *resinfo);
    int32_t __Rdata2SOA(DNS_RESOURCE_INFO *resinfo);
    int32_t __Rdata2MB(DNS_RESOURCE_INFO *resinfo);
    int32_t __Rdata2MG(DNS_RESOURCE_INFO *resinfo);
    int32_t __Rdata2MR(DNS_RESOURCE_INFO *resinfo);
    int32_t __Rdata2NULL(DNS_RESOURCE_INFO *resinfo);
    int32_t __Rdata2WKS(DNS_RESOURCE_INFO *resinfo);
    int32_t __Rdata2PTR(DNS_RESOURCE_INFO *resinfo);
    int32_t __Rdata2HINFO(DNS_RESOURCE_INFO *resinfo);
    int32_t __Rdata2MINFO(DNS_RESOURCE_INFO *resinfo);
    int32_t __Rdata2MX(DNS_RESOURCE_INFO *resinfo);
    int32_t __Rdata2TXT(DNS_RESOURCE_INFO *resinfo);
    /* ----------解析各类RDATA数据end----------- */
    
};

#endif  /* _DNSPACKET_H_ */
